package com.jiujichaoshi.oauth.server.controller;

import com.jiujichaoshi.oauth.server.security.ExtendAuthenticationToken;
import com.jiujichaoshi.oauth.server.security.verify.Verify;
import com.jiujichaoshi.oauth.server.security.verify.VerifyType;
import com.jiujichaoshi.oauth.server.vo.TokenResult;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.OAuth2Request;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;

/**
 * 登录。
 * 使用账号密码、短信，微信H5 code，微信小程序code登录，换取token。注意所有登录方法需要处理登录失败的异常，返回给前端。
 *
 * @author Yl
 */
@Api(tags = "登录接口")
@RestController
@RequestMapping("login")
public class LoginController {
    @Autowired
    private DefaultTokenServices tokenService;
    @Autowired
    private AuthenticationManager authenticationManager;

    /**
     * 使用账号密码登录
     *
     * @param userName 用户账号
     * @param password 用户密码
     * @param request  需要其他参数，传入请求对象
     * @return token和其他信息
     */
    @ApiOperation("账号密码登录")
    @ApiImplicitParams({
            @ApiImplicitParam(value = "账号", name = "userName", dataType = "String", paramType = "form"),
            @ApiImplicitParam(value = "密码", name = "password", dataType = "String", paramType = "form"),
    })
    @Verify
    @PostMapping("form")
    public TokenResult loginByPassword(String userName, String password, HttpServletRequest request) {
        UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(userName, password);
        //进行账号密码的认证
        Authentication authentication = authenticationManager.authenticate(usernamePasswordAuthenticationToken);
        //生成已完成认证的oAuth2 authentication信息
        OAuth2Authentication oAuth2Authentication = new OAuth2Authentication(new OAuth2Request(new HashMap<>(6), "sample", null, true, null, null, null, null, null), authentication);
        //生成access token
        OAuth2AccessToken accessToken = tokenService.createAccessToken(oAuth2Authentication);
        TokenResult result = new TokenResult();
        result.setAccessToken(accessToken);
        result.setRedirectUri(request.getParameter("redirect"));
        return result;
    }

    /**
     * 使用短信登录
     *
     * @param mobile   手机号
     * @param captcha  短信验证码
     * @param redirect 登录后重定向地址
     * @return token和其他信息
     */
    @ApiOperation("短信登录")
    @ApiImplicitParams({
            @ApiImplicitParam(value = "手机号", name = "mobile", dataType = "String", paramType = "form"),
            @ApiImplicitParam(value = "短信", name = "captcha", dataType = "String", paramType = "form"),
            @ApiImplicitParam(value = "登录后重定向地址", name = "redirect", dataType = "String", paramType = "form")
    })
    @Verify(value = VerifyType.NET_EASY)
    @PostMapping("sms")
    public TokenResult loginByMobile(String mobile, String captcha, String redirect) {
        ExtendAuthenticationToken authenticationToken = new ExtendAuthenticationToken(mobile, captcha, "sms");
        //进行账号密码的认证
        Authentication authentication = authenticationManager.authenticate(authenticationToken);
        //生成已完成认证的oAuth2 authentication信息
        OAuth2Authentication oAuth2Authentication = new OAuth2Authentication(new OAuth2Request(new HashMap<>(6), "sample", null, true, null, null, null, null, null), authentication);
        //生成access token
        OAuth2AccessToken accessToken = tokenService.createAccessToken(oAuth2Authentication);
        TokenResult result = new TokenResult();
        result.setAccessToken(accessToken);
        result.setRedirectUri(redirect);
        return result;
    }

    /**
     * 微信授权登录或扫码登录，此地址需要配置到微信开放平台中对应web应用的回调地址。注意：
     * 1. 如果是微信之外的浏览器，使用微信开放平台的appid和secret获取accessToken，再获取用户信息。
     * 2. 如果是微信浏览器，使用公众号的appid和secret获取accessToken。
     * <p>
     * 前端通过JS SDK构建登录二维码或授权请求，用户扫码或者点击授权后，微信服务器携带code跳转本连接。
     *
     * @param code 前端请求得到的授权code
     * @return token
     */
    @ApiOperation("微信登录-H5或扫码")
    @ApiImplicitParams({
            @ApiImplicitParam(value = "微信回调code", name = "code", dataType = "String", paramType = "form")
    })
    @PostMapping("wechat/h5")
    public OAuth2AccessToken loginWechatH5(String code) {
        //此处进行微信openId或uniId登陆，获取本地用户权限
        Authentication authentication = new UsernamePasswordAuthenticationToken("zhangsan", "123456");
        authentication.setAuthenticated(true);
        //生成已完成认证的oAuth2 authentication信息
        OAuth2Authentication oAuth2Authentication = new OAuth2Authentication(new OAuth2Request(new HashMap<>(6), "sample", null, true, null, null, null, null, null), authentication);
        //生成access token
        OAuth2AccessToken accessToken = tokenService.createAccessToken(oAuth2Authentication);
        return accessToken;
    }

    /**
     * 小程序登录.
     * 1. 前端调用 wx.login()获取临时登录凭证code，并回传到开发者服务器。
     * 2. 后端使用code调用 auth.code2Session 接口，换取用户唯一标识 OpenID 、 用户在微信开放平台帐号下的唯一标识UnionID（若当前小程序已绑定到微信开放平台帐号）和会话密钥session_key，绑定微信用户和系统登录关系。
     *
     * @param code 小程序前端获取的code
     * @return token
     */
    @ApiOperation("微信小程序登录")
    @ApiImplicitParams({
            @ApiImplicitParam(value = "前端code", name = "code", dataType = "String", paramType = "form")
    })
    @PostMapping("wechat/app")
    public OAuth2AccessToken loginWechatApp(String code) {
        //使用code获取小程序session，获取用户openId,uniId，进行登陆，并关联本地session
        Authentication authentication = new UsernamePasswordAuthenticationToken("zhangsan", "123456");
        authentication.setAuthenticated(true);
        //生成已完成认证的oAuth2 authentication信息
        OAuth2Authentication oAuth2Authentication = new OAuth2Authentication(new OAuth2Request(new HashMap<>(6), "sample", null, true, null, null, null, null, null), authentication);
        //生成access token
        OAuth2AccessToken accessToken = tokenService.createAccessToken(oAuth2Authentication);
        return accessToken;
    }
}
